﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Toolbox.Plugin
{
    public partial class PluginRunner : Component, IPluginRunner
    {
        class AsyncWrapper : IDisposable
        {
            private IPluginAction p;
            private object[] o;

            public AsyncWrapper(IPluginAction p, params object[] o)
            {
                this.p = p.Clone() as IPluginAction;

                if (o is object[])
                {
                    this.o = o;
                }
                else
                {
                    this.o = new object[0];
                }
            }

            public object Execute()
            {
                return this.p.DoAsync(o);
            }

            #region IDisposable Membres

            public void Dispose()
            {
                this.p.Dispose();
            }

            #endregion
        }

        public PluginRunner()
        {
            InitializeComponent();
            this.DelayedCalls = new Queue<AsyncWrapper>();
        }

        #region IPluginRunner Membres

        [Browsable(false)]
        public bool IsBusy { get { return this.DelayedCalls.Count > 0; } }

        [Category("Plugin Management")]
        public event RunWorkerCompletedEventHandler Done;

        [Category("Plugin Management")]
        public event EventHandler Started;

        public void RunAsync(IPluginAction p, params object[] args)
        {
            if (p is IPluginAction)
            {
                this.DelayedCalls.Enqueue(new AsyncWrapper(p, args));
                this.RunNext();
            }
            else
            {
                throw new ArgumentNullException("Not a IPluginAction plugin");
            }
        }

        public virtual void Cancel()
        {
            this.DelayedCalls.Clear();
            this.AsyncOperation.CancelAsync();
        }

        #endregion

        #region Async

        private Queue<AsyncWrapper> DelayedCalls;

        private void RunNext()
        {
            if (!this.AsyncOperation.IsBusy && this.DelayedCalls.Count > 0)
            {
                this.AsyncOperation.RunWorkerAsync(this.DelayedCalls.Dequeue());

                if (this.Started != null)
                {
                    this.Started(this, EventArgs.Empty);
                }

            }
        }

        private void AsyncOperation_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (e.Argument is AsyncWrapper)
                {
                    e.Result = (e.Argument as AsyncWrapper).Execute();
                }
            }
            catch (Exception ex)
            {
                e.Result = ex;
                e.Cancel = true;
            }
        }

        private void AsyncOperation_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.OnRunWorkerCompleted(e);
        }

        protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
        {
            if (this.Done != null)
            {
                this.Done(this, e);
            }

            RunNext();
        }

        #endregion


    }
}
